// Renzoderer.cpp : Defines the exported functions for the DLL application.
//

#include "stdafx.h"
#include "RenzoPrecompiled.h"
#include "Renzoderer.h"
#include "RenzoDynLib.h"
#include "RenzoDynLibManager.h"
#include "RenzoPlugin.h"
#include "RenzoRenderSystem.h"
#include "RenzoImageManager.h"
#include "RenzoResourceManager.h"
#include "RenzoSceneManager.h"
#include "RenzoTimer.h"
#include "RenzoScene.h"
#include "RenzoMeshMd2.h"

#include <algorithm>		// std::find


namespace Renzo {
/*
 * Singleton and static variables declaration
 */
Renzoderer*		Renzoderer::renzo	= 0;
Log*			Renzoderer::log		= 0;

Renzoderer* Renzoderer::instance() {
	if (renzo == 0) {
	//#ifdef RENZO_GL
		// find RenzodererGL.dll and install this plugin
		//renzo = new RenzodererGL;
		renzo = new Renzoderer;
	//#endif
	}
	return renzo;
}

Renzoderer::Renzoderer()
{
	// TODO: create all singleton objects in Renzoderer here. 
	// 2009-09-28: It's singleton, why bother create its objects?
	log = Log::instance();

	// reset startup arguments
	argc = 0;
	argv = 0;
	
	timer = new Timer;
	resourceManager = new ResourceManager;
	resourceManager->registerCreateFuncs();

	sceneManager = new SceneManager;
	sceneManager->registerFormat(new MeshMd2);
}

Renzoderer::~Renzoderer() {
	rzDel(timer);
	rzDel(resourceManager);
	rzDel(sceneManager);
}

void Renzoderer::kickoff() {
	log->write("Renzoderer kickoff.");
	// check if any render systems are available. If yes, start it.
	if (renderSystems.size() > 0) {
		if (activeRenderSystem == 0) {
			// TODO:
			// automatically determine a render system
			// or show a dialog here
		}
		// fire up the renderer
		activeRenderSystem->render();			
	}
	log->write("Renzoderer ended.");
}

void Renzoderer::loadPlugin(const char* dll) {
	// track this plugin dll
	PluginDynLibList::iterator i = loadedPluginDlls.find(dll);
	if (i != loadedPluginDlls.end()) { // already loaded, do nothing
	} else {
		// load this dll
		DynLib* d = DynLibManager::instance()->load(dll);
		// get function pointer
		DLL_START_PLUGIN pFunc = (DLL_START_PLUGIN)d->getSymbol(SYMBOL_DLL_START);
		// call "constructor"
		pFunc();
		// add to list
		loadedPluginDlls[dll] = d;
	}
}

void Renzoderer::unloadPlugin(const char *dll) {
	// track this plugin dll
	PluginDynLibList::iterator i = loadedPluginDlls.find(dll);
	if (i != loadedPluginDlls.end()) { // already loaded, can unload
		DynLib* d = i->second;
		// get function pointer
		DLL_STOP_PLUGIN pFunc = (DLL_STOP_PLUGIN)d->getSymbol(SYMBOL_DLL_STOP);
		// call "destructor"
		pFunc();
		// unload
		DynLibManager::instance()->unload(d);
		d = NULL;
		// remove from list
		loadedPluginDlls.erase(i);
	}
}

void Renzoderer::installPlugin(Plugin* p) {
	Log::instance()->tabWrite("Installing plugin: %s", p->getName());
	// add to list
	installedPlugins.push_back(p);
	// install
	p->install();
	Log::instance()->untabWrite("Plugin %s installed.", p->getName());
}

void Renzoderer::uninstallPlugin(Plugin* p) {
	Log::instance()->tabWrite("Uninstalling plugin: %s", p->getName());
	PluginInstanceList::iterator i = std::find(installedPlugins.begin(), installedPlugins.end(), p);
	if (i != installedPlugins.end()) {
		// uninstall
		p->uninstall();
		// remove from list
		installedPlugins.erase(i);
	}
	Log::instance()->untabWrite("Plugin uninstalled.");
}

void Renzoderer::addRenderSystem(RenderSystem* r) {	
	renderSystems.push_back(r);
	Log::instance()->write("Render system %s added.", r->getName());
	//activeRenderSystem = r; // side effect (add render and auto set active render system)
}

RenderSystem* Renzoderer::getActiveRenderSystem() {
	return activeRenderSystem;
}

void Renzoderer::setActiveRenderSystem(RenderSystem* r) {
	rzAssert(r != 0);
	activeRenderSystem = r;
	Log::instance()->write("Using render system %s.", r->getName());
}

void Renzoderer::setRenderSystemByName(const char* name) {
	setActiveRenderSystem(getRenderSystemByName(name));
}

RenderSystem* Renzoderer::getRenderSystemByName(const char* name) {
	RenderSystemList::iterator i;
	for (i = renderSystems.begin(); i != renderSystems.end(); i++) {
		RenderSystem* r = *i;
		if (strcmp(r->getName(), name) == 0) {
			return r;
		}
	}
	return 0;
}

void Renzoderer::getArguments(int* _argc, char*** _argv) {
	*_argc = argc;
	*_argv = argv;
}

void Renzoderer::setArguments(int _argc, char* _argv[]) {
	argc = _argc;
	argv = _argv;
}

void Renzoderer::addScene(Scene *s) {
	// TODO: check to avoid duplicate scene addition (by name)
	scenes.push_back(s);
}

void Renzoderer::setActiveScene(Scene *s) {
	activeScene = s;
}

Scene* Renzoderer::getActiveScene() const {
	return activeScene;
}

Timer* Renzoderer::getTimer() const {
	return timer;
}

int Renzoderer::update() {
	int delta = timer->Update();
	// update every scene
	activeScene->update(delta);

	return delta;
}

int Renzoderer::getWindowWidth() {
	return winWidth;
}

int Renzoderer::getWindowHeight() {
	return winHeight;
}

void Renzoderer::setWindowSize(int w, int h) {
	winWidth = w;
	winHeight = h;
}

ImageManager* Renzoderer::getImageManager() const {
	return imageManager;
}

void Renzoderer::setImageManager(ImageManager* im) {
	imageManager = im;
}

TextureManager* Renzoderer::getTextureManager() const {
	return textureManager;
}

void Renzoderer::setTextureManager(TextureManager* tm) {
	textureManager = tm;
}

ResourceManager* Renzoderer::getResourceManager() const {
	return resourceManager;
}

SceneManager* Renzoderer::getSceneManager() const {
	return sceneManager;
}

}